﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection.Metadata.Ecma335;
using System.Text;
using System.Threading.Tasks;

namespace SimpleScript
{
    internal class Lexer
    {
        public int Line { set; get; }
        public int Column { set; get; }
        public string Code { set; get; }

        public char Character { set; get; }
        public int Position { set; get; }

        public Lexer(string codePath)
        {
            Code = File.ReadAllText(codePath);
            Line = 1;
            Column = 1;
            Position = 0;
        }

        public List<Token> TokenList
        {
            get
            {
                List<Token> list = new();
                while (true)
                {
                    Token? token = NextToken;
                    if (token == null)
                    {
                        break;
                    }
                    list.Add(token);
                }
                return list;
            }
        }

        public Token? NextToken
        {
            get
            {
                ReadCharacter();
                SkipWhiteSpace();
                if (Character == '\0')
                {
                    return null;
                }

                string literal = "";
                LexicalType lexicalType = LexicalType.None;

                switch (Character)
                {
                    #region 运算符
                    case '+':
                        lexicalType = LexicalType.Add;
                        literal = "+";
                        break;
                    case '-':
                        lexicalType = LexicalType.Sub;
                        literal = "-";
                        break;
                    case '*':
                        lexicalType = LexicalType.Mul;
                        literal = "*";
                        break;
                    case '/':
                        lexicalType = LexicalType.Div;
                        literal = "/";
                        break;
                    case '%':
                        lexicalType = LexicalType.Mod;
                        literal = "%";
                        break;

                    case '=':
                        lexicalType = LexicalType.Equ;
                        literal = "=";
                        break;

                    case '\"':
                        lexicalType = LexicalType.LString;
                        literal = NextString;
                        break;

                    #endregion

                    default:
                        #region 数字
                        if (IsDigit(Character))
                        {
                            literal = NextNumber;
                            if (literal.Count(c => IsPoint(c)) == 1)
                            {
                                lexicalType = LexicalType.LFloat;
                            }
                            else
                            {
                                lexicalType = LexicalType.LInt;
                            }
                        }
                        #endregion
                        #region 标识符
                        else if (IsIdentifierStart(Character))
                        {
                            literal = NextIdentifier;
                            if (IsKeyword(literal))
                            {
                                lexicalType = Keyword[literal];
                            }
                            else
                            {
                                lexicalType = LexicalType.Identifier;
                            }
                        }
                        else
                        {
                            throw new Exception($"[Line:{Line},Column:{Column}]=>[{Character}]使用了不受支持的字符");
                        }
                        break;
                        #endregion
                }

                Token token = new()
                {
                    Literal = literal,
                    LexicalType = lexicalType,
                    Line = Line,
                    Column = Column,
                };

                Column += literal.Length;
                if (lexicalType == LexicalType.LString)
                {
                    Column += 2;
                }

                return token;
            }
        }

        public static Dictionary<string, LexicalType> Keyword = new()
        {
            {"int",LexicalType.Int },{"整数",LexicalType.Int },
            {"flaot",LexicalType.Float },{"小数",LexicalType.Float },
            {"string",LexicalType.String },{"字符串",LexicalType.String },
            {"input",LexicalType.Input },{"输入",LexicalType.Input },
            {"output",LexicalType.Output },{"输出",LexicalType.Output },
            {"temp",LexicalType.Temp },{"临变",LexicalType.Temp },
        };

        public static bool IsKeyword(string identifer)
        {
            return Keyword.ContainsKey(identifer);
        }

        public void ReadCharacter()
        {
            if (Position < Code.Length)
            {
                Character = Code[Position];
                Position++;
            }
            else
            {
                Character = '\0';
            }
        }

        public char NextCharacter
        {
            get
            {
                if (Position < Code.Length)
                {
                    return Code[Position];
                }
                else
                {
                    return '\0';
                }
            }
        }

        public static bool IsDigit(char c)
        {
            return char.IsDigit(c);
        }

        public static bool IsLetter(char c)
        {
            return char.IsLetter(c) || c >= '\u4e00' && c <= '\u9fa5';
        }

        public static bool IsPoint(char c)
        {
            return c == '.';
        }

        public static bool IsUnderline(char c)
        {
            return c == '_';
        }

        public static bool IsIdentifierStart(char c)
        {
            return IsLetter(c) || IsUnderline(c);
        }

        public static bool IsIdentifierEnd(char c)
        {
            return IsIdentifierStart(c) || IsDigit(c);
        }

        public string NextNumber
        {
            get
            {
                string literal = "" + Character;
                while (IsDigit(NextCharacter) || IsPoint(NextCharacter))
                {
                    ReadCharacter();
                    literal += Character;
                }
                return literal;
            }
        }

        public string NextString
        {
            get
            {
                ReadCharacter();
                string literal = "";
                while (Character != '\"')
                {
                    if (Character == '\\' && NextCharacter == '\"')
                    {
                        literal += '\"';
                        ReadCharacter();
                    }
                    else
                    {
                        literal += Character;
                    }
                    ReadCharacter();
                }
                return literal;
            }
        }

        public string NextIdentifier
        {
            get
            {
                string literal = "" + Character;
                while (IsIdentifierEnd(NextCharacter))
                {
                    ReadCharacter();
                    literal += Character;
                }
                return literal;
            }
        }

        public void SkipWhiteSpace()
        {
            while (char.IsWhiteSpace(Character))
            {
                switch (Character)
                {
                    case ' ':
                        Column++;
                        break;
                    case '\r':
                        break;
                    case '\n':
                        Line++;
                        Column = 1;
                        break;
                    case '\t':
                        Column += 4;
                        break;
                    default:
                        break;
                }
                ReadCharacter();
            }
        }
    }
}
